﻿using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc;
using System.Reflection;
using System.Runtime.CompilerServices;
using Microsoft.AspNetCore.Http;

namespace Magic.Core;

/// <summary>
/// 自定义应用程序的元数据
/// </summary>
public class ApplicationModelConvention : IApplicationModelConvention
{
    public void Apply(ApplicationModel application)
    {
        var restfulResultType = typeof(RestfulResult<>);
        foreach (var controller in application.Controllers)
        {
            var controllerType = controller.ControllerType;

            // 判断是否处理 Mvc控制器
            if (typeof(ControllerBase).IsAssignableFrom(controllerType))
            {
                foreach (var action in controller.Actions)
                {
                    // 获取真实类型
                    var returnType = GetRealReturnType(action.ActionMethod);
                    if (returnType != typeof(void))
                    {
                        // 配置动作方法规范化特性

                        action.Filters.Add(WrapType(returnType, restfulResultType));
                    }
                    
                }
            }
        }
    }

    private ProducesResponseTypeAttribute WrapType(Type returnType, Type restfulResultType) {
        ProducesResponseTypeAttribute producesResponseType=new ProducesResponseTypeAttribute(returnType, StatusCodes.Status200OK);
        producesResponseType.Type= restfulResultType.MakeGenericType(returnType);
        return producesResponseType;
    }

    /// <summary>
    /// 获取方法真实返回类型
    /// </summary>
    /// <param name="method"></param>
    /// <returns></returns>
    private Type GetRealReturnType(MethodInfo method)
    {
        // 判断是否是异步方法
        var isAsyncMethod = IsAsync(method);

        // 获取类型返回值并处理 Task 和 Task<T> 类型返回值
        var returnType = method.ReturnType;
        return isAsyncMethod ? (returnType.GenericTypeArguments.FirstOrDefault() ?? typeof(void)) : returnType;
    }

    private bool IsAsync(MethodInfo method)
    {
        return method.GetCustomAttribute<AsyncMethodBuilderAttribute>() != null
            || method.ReturnType.ToString().StartsWith(typeof(Task).FullName);
    }
}